﻿using System;
using System.Text;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace KorleBu
{
    public static class PasswordHash
    {
        /// <summary>
        /// Method creates a salt and pre appends it to the password bytes, then creates a hashed password.
        /// </summary>
        /// <param name="password">user password to be hashed</param>
        /// <param name="dbSalt">salt from the database used to hash the particular user's password, can be null</param>
        /// <returns>string [] of salt and hashed password</returns>
        public static string[] createHashedPassword(string password, string dbSalt = null)
        {
            //create a salt byte array
            byte[] salt;

            //if there wasn't any database salt, then generate a salt to create a newly hashed password.
            //Otherwise, use the database salt, to create the hashed password to confirm against the login password.
            if (dbSalt == null)
            {
                // Generate a random salt
                RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
                salt = new byte[64];
                csprng.GetBytes(salt);
            }
            else
            {
                salt = Convert.FromBase64String(dbSalt);
            }

            HashAlgorithm hash = HashAlgorithm.Create("SHA1");                    // Hash algorithm for hashing created or modified passwords.

            byte[] passwordBytes = Encoding.Unicode.GetBytes(password);             //encode the password into bytes
            byte[] passwordWithSalt = salt.Concat(passwordBytes).ToArray();         //pre append salt to password bytes
            byte[] hashedPassword = hash.ComputeHash(passwordBytes);                //hash the password

            //combine the strings of the salt and hashed password using a delimiter, then split the string into a string array.
            string longPassword = Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hashedPassword);
            string[] delimiter = { ":" };

            // return the encode string []
            return longPassword.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
        }

        /// <summary>
        /// Method checks the length of the newly hashed password against the saved hashed password.
        /// Also, checks whether the passwords are empty byte arrays.
        /// </summary>
        /// <param name="newlyHashedPassword">the newly hashed password to compare to the old</param>
        /// <param name="hashedPassword">the old hashed password</param>
        /// <returns>true if the passwords are the same, false otherwise</returns>
        public static bool CompareHashedPassword(string[] newlyHashedPassword, string hashedPassword)
        {
            byte[] newSalt = Convert.FromBase64String(newlyHashedPassword[0]);      //get the salt bytes of the new password or the password to be checked
            byte[] newHash = Convert.FromBase64String(newlyHashedPassword[1]);      //get the hash bytes of the new password or the password to be checked

            byte[] newPasswordWithSalt = newSalt.Concat(newHash).ToArray();         //combine the salt bytes and hash bytes into one byte array 

            //split the salt and hash of the old password or the password to check the new password.
            string[] delimiter = { ":" };
            string[] split = hashedPassword.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);

            byte[] oldSalt = Convert.FromBase64String(split[0]);                    //get the salt bytes of the new password or the password to be checked
            byte[] oldHash = Convert.FromBase64String(split[1]);                    //get the hash bytes of the new password or the password to be checked

            byte[] oldPasswordWithSalt = oldSalt.Concat(oldHash).ToArray();         //combine the salt bytes and hash bytes into one byte array 

            //verify the new password against the old password
            if (newPasswordWithSalt.Length != oldPasswordWithSalt.Length || oldPasswordWithSalt == null || newPasswordWithSalt == null)
                return false;
            else
                return newPasswordWithSalt.SequenceEqual(oldPasswordWithSalt);
        }
    }
}
